(ns run
  (:refer-clojure)
  (:require [clojure.string :as string]
            [shen.primitives])
  (:use [clojure.java.io :only (file reader writer)]
        [clojure.pprint :only (pprint)])
  (:import [java.util.regex Pattern]
           [java.io StringReader PushbackReader])
  (:gen-class))

(def shen-files '[sys writer declarations core load macros prolog reader sequent toplevel track t-star printer yacc types])

;;; Shen execute commands

;; replaces all occurrences of "unpleasant symbols"
;; by symbols that can be used in Clojure
(def special-form-replacement-rules 
         (re-pattern (str "\\b("
                         (string/join "|" ["def" "var" 
                                           "fn" "loop" "recur" 
                                           "throw" "try" 
                                           "monitor-enter" "monitor-exit"])
                         ")\\b")))
         
(def cleanup-symbols-pattern
  (re-pattern (str "(\\s+|\\(|\\[)("
                   (string/join "|" (map #(Pattern/quote %) [":" ";" "{" "}" ":-" ":="
                                                             "/." 
                                                             "shen-i/o-macro"
                                                             "shen-put/get-macro"
                                                             "XV/Y"]))
                   ")(\\s*\\)|\\s+?)"
                   "(?!~)")))

(defn replaceall
  [kl] (string/replace 
                (string/replace  
                 (string/replace 
                  kl 
                  cleanup-symbols-pattern "$1(intern \"$2\")$3") 
                 special-form-replacement-rules "kl_$1")
                 "@" "__at__"))
        
(def kl-path "klambda/")

(defn read-kl [kl]
  (with-open [r (PushbackReader. (StringReader. (replaceall kl)))]
    (doall
     (take-while (complement nil?)
                 (repeatedly #(read r false nil))))))

(defn read-kl-file [file]
  (try
    (cons `(clojure.core/comment ~(str file)) (read-kl (slurp file)))
    (catch Exception e
      (println file e))))

(defn write-clj-file [dir name forms]
  (with-open [w (writer (file dir (str name ".clj")))]
    (binding [*out* w]
      (doseq [f forms]
        (pprint f)
        (println)))))

(def missing-declarations '#{shen-kl-to-lisp FORMAT READ-CHAR})

(defn declarations [clj]
  (into missing-declarations
        (map second (filter #(= 'defun (first %)) clj))))

(defn run []
  (let [kl (mapcat #(read-kl-file (str kl-path % ".kl"))
                   shen-files)
        output (concat [`(~'ns ~'shen
                               (:refer-clojure :only [])
                               (:use [shen.primitives]  [clojure.tools.trace])
                               (:require [clojure.core :as ~'cl]
										 [clojure.walk :as ~'walk])
                               (:gen-class))]
                       [`(declare ~@(filter symbol? (declarations kl)))]
                       (map #(shen.primitives/pre-eval-without-macros %)
                            (remove string? kl))
                       ['(clojure.core/defn -main []
                                            (shen-shen))]
                       [`(clojure.core/load "shen/overwrites")])]
    (write-clj-file "src" "shen" output)))

;(load "shen/testcases")

(defn install []
 (try 
    (require 'shen)
    (catch Exception _
      (run)
      (require 'shen))))

	(when *compile-files*
		(install))

(defn -main []
  (install)
  (binding [*ns* (the-ns 'shen)]
    ((resolve 'shen/-main)))) 
